package edu.hawaii.senin.iclouds.basics;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.List;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.junit.Before;
import org.junit.Test;

/**
 * Tests normal Atom operations.
 * 
 * @author Pavel Senin.
 * 
 */
public class TestAtom {

  /**
   * test Atom1.
   */
  private Atom atom1;
  private static final String name1 = "H";
  private static final Double amu1 = 1.008D;
  private static final Double abundance1 = Double.valueOf(0.33D);
  private static final Double fraction1 = null;
  private static final String fractionOf1 = null;

  /**
   * test Atom2.
   */
  private Atom atom2;
  private static final String name2 = "D";
  private static final Double amu2 = 2.04D;
  private static final Double abundance2 = Double.valueOf(0D);
  private static final Double fraction2 = Double.valueOf(1.0E-3D);
  private static final String fractionOf2 = "H";

  private static final String fileName1 = "config/test_cfg/testIO01.xml";

  /**
   * Little test fixture.
   */
  @Before
  public void runBeforeEachTest() {
    // public Atom(String name, Double amu, Double abundance, Double fraction, String frOf,
    // Double accretionProbability, Double velocity, String type, Long accumulator) {
    atom1 = new Atom(name1, amu1, abundance1, fraction1, fractionOf1, 0D, 0D, "active", 0L);
    atom2 = new Atom(name2, amu2, abundance2, fraction2, fractionOf2, 0D, 0D, "passive", 0L);
    // population.add(atom1.getName(), atom1, amount)
  }

  /**
   * Tests getters and setters.
   * 
   */
  @Test
  public void testSetAndGetMethods() {
    // private final String name1 = "H";
    // private final Double amu1 = 1.008D;
    // private final Double abundance1 = Double.valueOf(0.33D);
    // private final Double fraction1 = Double.valueOf(0D);
    // private final String fractionOf1 = "";
    assertEquals("Testing name getter", "H", atom1.getName());
    assertEquals("Testing amu getter", 1.008D, atom1.getAmu());
    assertEquals("Testing amu getter", 0.33D, atom1.getAbundance());
    assertNull("Testing amu getter", atom1.getFraction());
    assertNull("Testing amu getter", atom1.getFractionOf());
    atom1.setAccretionProbability(0.22D);
    assertEquals("Testing accretion probability getter", 0.22D, atom1.getAccretionProbability());
    atom1.setVelocity(0.11D);
    assertEquals("Testing velocity getter", 0.11D, atom1.getVelocity());
    assertTrue("Testing isActive() method ", atom1.isActive());
    assertFalse("Testing isActive() method ", atom2.isActive());

    atom1.setVelocity(0.5D);
    Double rate1 = atom1.getAbundance() * atom1.getVelocity() * Math.PI * 0.5D;
    assertEquals("Testing getRate", rate1, atom1.getRate(0.5D));
  }

  /**
   * Tests comparisons.
   * 
   */
  @Test
  public void testHashesAndCompares() {
    assertTrue("Testing comparison operations", atom2.compareTo(atom1) < 0);
    assertTrue("Testing comparison operations", atom1.compareTo(atom2) > 0);
    assertFalse("Testing comparison operations", atom1.equals(atom2));
    assertFalse("Testing comparison operations", atom1.equals("zhaba"));
    assertTrue("Testing hash code operations", atom1.hashCode() == atom1.hashCode());
    assertFalse("Testing hash code operations", atom1.hashCode() == atom2.hashCode());

    Atom atom3 = atom1.clone();
    assertEquals("Testing comparison operations", atom1, atom3);
  }

  /**
   * Tests accumulator operations.
   */
  @Test
  public void testAccumulator() {
    atom1.setAccumulatorValue(10L);
    atom1.incrementAccumulatorValue(1L);
    assertEquals("Testing accumulator operations", 11L, atom1.getAccumulatorValue());
    atom1.incrementAccumulatorValue(9L);
    assertEquals("Testing accumulator operations", 20L, atom1.getAccumulatorValue());
    atom1.decrementAccumulatorValue(9L);
    assertEquals("Testing accumulator operations", 11L, atom1.getAccumulatorValue());
    atom1.decrementAccumulatorValue(1L);
    assertEquals("Testing accumulator operations", 10L, atom1.getAccumulatorValue());
  }

  /**
   * Test IO rutines.
   * 
   */
  @Test
  public void testIOStuff() {
    atom1.setAccumulatorValue(10L);
    atom1.setAccretionProbability(0.8D);
    String shouldBe = "H:  amu 1.008, abundance 0.33, type: active, vel: 0.0, accum: 10\n";

    // System.out.println(atom1.toText());
    assertTrue("Testing toText", shouldBe.equalsIgnoreCase(atom1.toText()));

    shouldBe = "D:  amu 2.04, abundance 0.0, fr 0.0010, type: passive, vel: 0.0, accum: 0\n";
    // System.out.println(atom2.toText());
    assertTrue("Testing toText", shouldBe.equalsIgnoreCase(atom2.toText()));

    try {
      BufferedWriter writer = new BufferedWriter(new FileWriter(new File(TestAtom.fileName1)));
      writer.write("<population>");
      atom1.dump2XML(writer);
      atom2.dump2XML(writer);
      writer.write("</population>");
      writer.flush();
      writer.close();

      SAXBuilder builder = new SAXBuilder();
      BufferedReader reader = new BufferedReader(new FileReader(new File(TestAtom.fileName1)));
      Document popDoc = builder.build(reader);
      Element root = popDoc.getRootElement();
      if ((null == root) || (!root.getName().equals("population"))) {
        fail("got no or bad root element");
      }
      List atoms = root.getChildren();

      Atom atom3 = new Atom((Element) atoms.get(0));
      assertEquals("Testing IO operations", "H", atom3.getName());
      assertEquals("Testing IO operations", 1.008D, atom3.getAmu());
      assertEquals("Testing IO operations", 0.33D, atom3.getAbundance());
      assertNull("Testing IO operations", atom3.getFraction());
      assertNull("Testing IO operations", atom3.getFractionOf());
      assertTrue("Testing IO operations", atom3.isActive());

      Atom atom4 = new Atom((Element) atoms.get(1));
      // <atom>
      // <name>D</name>
      // <amu>2.04</amu>
      // <abundance>0.0</abundance>
      // <fraction>0.0010</fraction>
      // <fraction_of>H</fraction_of>
      // <type>passive</type>
      // </atom>
      assertEquals("Testing IO operations", "D", atom4.getName());
      assertEquals("Testing IO operations", 2.04D, atom4.getAmu());
      assertEquals("Testing IO operations", 0.0D, atom4.getAbundance());
      assertEquals("Testing IO operations", 0.001D, atom4.getFraction());
      assertEquals("Testing IO operations", "H", atom4.getFractionOf());
      assertFalse("Testing IO operations", atom4.isActive());

      reader.close();
    }
    catch (IOException e) {
      fail("Got IO exception " + e.getMessage());
    }
    catch (JDOMException e) {
      fail("Got JDOM exception " + e.getMessage());
    }
  }

}
